import LogicFlow from '@logicflow/core';
import GlobalVariableManager from './GlobalVariableManager';

/**
 * LogicFlow 节点类型
 */
type NodeConfig = {
  id: string;
  type: string;
  x: number;
  y: number;
  text?:
    | {
        x: number;
        y: number;
        value: string;
      }
    | string;
  properties?: Record<string, unknown>;
};

type Point = {
  x: number;
  y: number;
};

/**
 * LogicFlow 边类型
 */
type EdgeConfig = {
  id: string;
  type: string;
  sourceNodeId: string;
  targetNodeId: string;
  text?: {
    x: number;
    y: number;
    value: string;
  };
  startPoint?: {
    x: number;
    y: number;
  };
  endPoint?: {
    x: number;
    y: number;
  };
  pointsList?: Point[];
  properties: Record<string, unknown>;
};

/**
 * LogicFlow 图原始类型
 */
type LogicGraphRawData = {
  nodes: NodeConfig[];
  edges: EdgeConfig[];
};

type BehaviorTreeNode = {
  id: string;
  type: string;
  properties?: Record<string, any>;
  children: BehaviorTreeNode[];
};

type BehaviorTreeData = {
  id: string;
  treeData: BehaviorTreeNode;
};

/**
 * 行为树类型
 */
export type BehaviorTree = {
  /**
   * LogicFlow 图原始数据
   */
  rawData: LogicGraphRawData;
  /**
   * 行为树数据
   */
  treeData: BehaviorTreeData;
  /**
   * 未参与构建树, 游离的节点
   */
  separateNodes: NodeConfig[];
};

const genEmptyTree = (): BehaviorTreeNode => {
  return {
    id: 'root',
    type: 'Entry',
    children: [],
  };
};

/**
 * 行为树数据转换
 *
 * {@link https://site.logic-flow.cn/docs/#/zh/guide/extension/adapter?id=%e5%a6%82%e4%bd%95%e8%87%aa%e5%ae%9a%e4%b9%89%e6%95%b0%e6%8d%ae%e8%bd%ac%e6%8d%a2%e5%b7%a5%e5%85%b7}
 */
class BehaviorTreeAdapter {
  static pluginName = 'BehaviorTreeAdapter';

  constructor({ lf }: { lf: LogicFlow }) {
    lf.adapterIn = (data: any) => this.adapterIn(data);
    lf.adapterOut = (data: any) => this.adapterOut(data);
  }

  adapterIn = (data: BehaviorTree) => {
    GlobalVariableManager.setVariables({
      id: {
        type: 'string',
        value: data.treeData.id,
      },
    });
    return data.rawData;
  };

  adapterOut = (rawData: LogicGraphRawData): BehaviorTree => {
    const separateNodes: NodeConfig[] = [];
    const treeData = this.toTreeData(rawData, separateNodes);
    return {
      rawData,
      treeData,
      separateNodes,
    };
  };

  /**
   * 计算行为树 \
   * 构建树, 并按照节点 x 从左到右顺序添加节点(行为树的同一层级, 左侧优先级高)
   * @param rawData
   * @param separateNodes
   * @returns
   */
  toTreeData = (rawData: LogicGraphRawData, separateNodes: NodeConfig[]): BehaviorTreeData => {
    const { nodes, edges } = rawData;
    const rootConnection = edges.find((o) => o.sourceNodeId === 'root');
    const emptyTree = genEmptyTree();
    // 如果没有根节点, 返回空树, 所有节点都设置为游离节点
    if (!rootConnection) {
      separateNodes.push(...nodes);
      return {
        id: GlobalVariableManager.variables.id.value as string,
        treeData: emptyTree,
      };
    }

    /**
     * 游离的节点集合
     */
    const separateNodeSet = new Set<string>();

    // 创建节点映射
    const rawNodeMap: Record<string, NodeConfig> = {};
    const treeNodeMap: Record<string, BehaviorTreeNode> = {};
    nodes.forEach((node) => {
      const { id, type, properties } = node;
      separateNodeSet.add(id);
      rawNodeMap[id] = node;
      treeNodeMap[id] = {
        id,
        type,
        properties,
        children: [],
      };
    });

    // 构造树
    edges.forEach((edge) => {
      const { sourceNodeId, targetNodeId } = edge;
      const sourceNode = treeNodeMap[sourceNodeId];
      const targetNode = treeNodeMap[targetNodeId];
      sourceNode.children.push(targetNode);
    });

    // 对同级节点按从左到右排序
    for (const id in treeNodeMap) {
      treeNodeMap[id].children.sort((a, b) => rawNodeMap[a.id].x - rawNodeMap[b.id].x);
    }

    const treeData = treeNodeMap[rootConnection.sourceNodeId];

    // 计算游离的节点
    const treeNodes: BehaviorTreeNode[] = [treeData];
    while (treeNodes.length) {
      const treeNodePtr = treeNodes.shift()!;
      if (treeNodePtr.children.length) {
        treeNodes.push(...treeNodePtr.children);
      }
      separateNodeSet.delete(treeNodePtr.id);
    }
    const tmpSeparateNodes = nodes.filter((o) => separateNodeSet.has(o.id));
    separateNodes.push(...tmpSeparateNodes);

    return {
      id: GlobalVariableManager.variables.id.value as string,
      treeData,
    };
  };
}

export default BehaviorTreeAdapter;
